package com.competitions.videoedithelper.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.competitions.videoedithelper.dao.AudioInfoDao;
import com.competitions.videoedithelper.dao.RecognizeDao;
import com.competitions.videoedithelper.entity.ResponseModel;
import com.competitions.videoedithelper.service.IVoiceRecognizeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

/**
 * @author Mapler
 */
@Service
public class VoiceRecognizeServiceImpl implements IVoiceRecognizeService {

    /**
     * 地域ID，常量，固定值。
     */
    private static final String REGION_ID = "cn-shanghai";
    private static final String END_POINT_NAME = "cn-shanghai";
    private static final String PRODUCT = "nls-filetrans";
    private static final String DOMAIN = "filetrans.cn-shanghai.aliyuncs.com";
    private static final String API_VERSION = "2018-08-17";
    private static final String POST_REQUEST_ACTION = "SubmitTask";
    private static final String GET_REQUEST_ACTION = "GetTaskResult";
    /**
     * 请求参数
     */
    private static final String KEY_APP_KEY = "appkey";
    private static final String KEY_FILE_LINK = "file_link";
    private static final String KEY_VERSION = "version";
    private static final String AUTO_SPLIT = "auto_split";
    private static final String KEY_ENABLE_WORDS = "enable_words";
    /**
     * 响应参数
     */
    private static final String KEY_TASK = "Task";
    private static final String KEY_TASK_ID = "TaskId";
    private static final String KEY_STATUS_TEXT = "StatusText";
    private static final String KEY_RESULT = "Result";
    /**
     * 状态值
     */
    private static final String STATUS_SUCCESS = "SUCCESS";
    private static final String STATUS_RUNNING = "RUNNING";
    private static final String STATUS_QUEUEING = "QUEUEING";

    /**
     * 阿里云鉴权client
     */
    private IAcsClient client;

    private RecognizeDao iRecognizeDao;
    private AudioInfoDao iAudioInfoDao;

    @Autowired
    public VoiceRecognizeServiceImpl(RecognizeDao iRecognizeDao,AudioInfoDao iAudioInfoDao) {
        this.iRecognizeDao = iRecognizeDao;
        this.iAudioInfoDao = iAudioInfoDao;
    }

    @Override
    public ResponseModel recognizeVoice(JSONObject jsonObject) {
        try {
            init();
            String fileLink;
            int videoId;
            try{
                videoId = jsonObject.getInteger("videoId");
            }catch (Exception e) {
                 e.printStackTrace();
                 return new ResponseModel(400,"缺少必要参数");
            }
            fileLink = iAudioInfoDao.selectAudioUrl(videoId);
            if(fileLink == null){
                return new ResponseModel(186,"该视频暂未提取音频");
            }
            String appKey = "AEt053aSGXB6G4EX";
            // 第一步：提交录音文件识别请求，获取任务ID用于后续的识别结果轮询。
            String taskId = submitFileTransRequest(appKey, fileLink ,videoId);
            if(taskId == null){
                return new ResponseModel(189,"录音文件识别请求失败");
            }
            return new ResponseModel(200,"录音文件识别请求成功",taskId);
        }catch (Exception e){
            e.printStackTrace();
            return new ResponseModel(500,"未知错误");
        }
    }

    @Override
    public ResponseModel getRecognizeResult(String taskId) {
        // 第二步：根据任务ID轮询识别结果。
        JSONObject result = getFileTransResult(taskId);
        if (result != null) {
            return new ResponseModel(200,"录音文件识别结果查询成功",result);
        }
        result = JSON.parseObject(iRecognizeDao.selectResult(taskId));
        if (result != null) {
            return new ResponseModel(200,"录音文件识别结果查询成功",result);
        }
        return new ResponseModel(188,"录音文件识别结果查询失败");
    }

    @Override
    public ResponseModel checkRecognizeResult(int videoId) {
        try{
            String result = iRecognizeDao.selectResById(videoId);
            if(result != null){
                return new ResponseModel(200,"获取成功",JSONObject.parseObject(result));
            }
            return new ResponseModel(185,"无结果");
        }catch (Exception e){
            e.printStackTrace();
            return new ResponseModel(500,"未知错误");
        }
    }


    /**
     * 初始化DefaultAcsClient实例
     */
    private void init() {
        // 设置endpoint
        try {
            DefaultProfile.addEndpoint(END_POINT_NAME, REGION_ID, PRODUCT, DOMAIN);
        } catch (ClientException e) {
            e.printStackTrace();
        }
        //阿里云账号对应的accessKeyId，accessKeySecret
        String accessKeyId = "LTAI4GHDqZfmQD3iRWzkdvPM";
        String accessKeySecret = "F0AtrRIEx00hNRNW6fXIuiVw6klnvX";

        // 创建DefaultAcsClient实例并初始化
        DefaultProfile profile = DefaultProfile.getProfile(REGION_ID, accessKeyId, accessKeySecret);
        this.client = new DefaultAcsClient(profile);
    }

    /**
     * 提交语音转写请求
     * @param appKey 产品的appKey
     * @param fileLink 语音文件的链接
     * @return 返回本次上传的语音转写请求的taskID，taskId将用于查询语音转写的结果
     */
    private String submitFileTransRequest(String appKey, String fileLink,int videoId) {
        /*
         *1. 创建CommonRequest，设置请求参数。
         */
        CommonRequest postRequest = new CommonRequest();
        // 设置域名
        postRequest.setDomain(DOMAIN);
        // 设置API的版本号，格式为YYYY-MM-DD。
        postRequest.setVersion(API_VERSION);
        // 设置action
        postRequest.setAction(POST_REQUEST_ACTION);
        // 设置产品名称
        postRequest.setProduct(PRODUCT);
        /*
         * 2. 设置录音文件识别请求参数，以JSON字符串的格式设置到请求Body中。
         */
        JSONObject taskObject = new JSONObject();
        // 设置appKey
        taskObject.put(KEY_APP_KEY, appKey);
        // 设置音频文件访问链接
        taskObject.put(KEY_FILE_LINK, fileLink);
        // 新接入请使用4.0版本，已接入（默认2.0）如需维持现状，请注释掉该参数设置。
        taskObject.put(KEY_VERSION, "4.0");
        // 设置是否输出词信息，默认为false，开启时需要设置version为4.0及以上。
        taskObject.put(KEY_ENABLE_WORDS, true);
        taskObject.put(AUTO_SPLIT, false);
        String task = taskObject.toJSONString();
        System.out.println(task);
        // 设置以上JSON字符串为Body参数。
        postRequest.putBodyParameter(KEY_TASK, task);
        // 设置为POST方式的请求。
        postRequest.setMethod(MethodType.POST);
        /*
         * 3. 提交录音文件识别请求，获取录音文件识别请求任务的ID，以供识别结果查询使用。
         */
        String taskId = null;
        int success = 200;
        try {
            CommonResponse postResponse = client.getCommonResponse(postRequest);
            System.err.println("提交录音文件识别请求的响应：" + postResponse.getData());
            if (postResponse.getHttpStatus() == success) {
                JSONObject result = JSONObject.parseObject(postResponse.getData());
                String statusText = result.getString(KEY_STATUS_TEXT);
                if (STATUS_SUCCESS.equals(statusText)) {
                    taskId = result.getString(KEY_TASK_ID);
                }
            }
        } catch (ClientException e) {
            e.printStackTrace();
        }
        if(iRecognizeDao.selectTaskId(fileLink)!=null){
            iRecognizeDao.updateTaskId(fileLink,taskId,videoId);
        }else{
            iRecognizeDao.insertTaskId(fileLink,taskId,videoId);
        }
        return taskId;
    }

    /**
     * 获取语音转写的结果
     * @param taskId 语音转写请求的taskID
     * @return 返回结果
     */
    private JSONObject getFileTransResult(String taskId) {
        /*
         * 1. 创建CommonRequest，设置任务ID。
         */
        CommonRequest getRequest = new CommonRequest();
        // 设置域名
        getRequest.setDomain(DOMAIN);
        // 设置API版本
        getRequest.setVersion(API_VERSION);
        // 设置action
        getRequest.setAction(GET_REQUEST_ACTION);
        // 设置产品名称
        getRequest.setProduct(PRODUCT);
        // 设置任务ID为查询参数
        getRequest.putQueryParameter(KEY_TASK_ID, taskId);
        // 设置为GET方式的请求
        getRequest.setMethod(MethodType.GET);
        /*
         * 2. 提交录音文件识别结果查询请求
         * 以轮询的方式进行识别结果的查询，直到服务端返回的状态描述为“SUCCESS”或错误描述，则结束轮询。
         */
        String result = null;
        while (true) {
            try {
                CommonResponse getResponse = client.getCommonResponse(getRequest);
                if (getResponse.getHttpStatus() != 200) {
                    break;
                }
                JSONObject rootObj = JSONObject.parseObject(getResponse.getData());
                String statusText = rootObj.getString(KEY_STATUS_TEXT);
                if (STATUS_RUNNING.equals(statusText) || STATUS_QUEUEING.equals(statusText)) {
                    // 继续轮询，注意设置轮询时间间隔。
                    Thread.sleep(10000);
                }
                else {
                    // 状态信息为成功，返回识别结果；状态信息为异常，返回空。
                    if (STATUS_SUCCESS.equals(statusText)) {
                        result = rootObj.getString(KEY_RESULT);
                        // 状态信息为成功，但没有识别结果，则可能是由于文件里全是静音、噪音等导致识别为空。
                        if(result == null) {
                            result = "";
                        }
                    }
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        JSONObject jsonObject;
        if(result!=null&& !"".equals(result.trim())){
            System.out.println(result);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
            jsonObject = JSONObject.parseObject(result);
            long timeDifference = TimeZone.getDefault().getRawOffset();
            JSONArray words = jsonObject.getJSONArray("Words");
            List<JSONObject> wordsList = new ArrayList<>();
            JSONArray sentences = jsonObject.getJSONArray("Sentences");
            List<JSONObject> sentencesList = new ArrayList<>();
            transformTime(simpleDateFormat, timeDifference, words, wordsList);
            transformTime(simpleDateFormat, timeDifference, sentences, sentencesList);
            jsonObject.put("Words",wordsList);
            jsonObject.put("Sentences",sentencesList);
            iRecognizeDao.updateResult(taskId,jsonObject.toString());
            return jsonObject;
        }
        return null;
    }

    private void transformTime(SimpleDateFormat simpleDateFormat, long timeDifference, JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        for (Iterator<Object> iterator = jsonArray.iterator(); iterator.hasNext();) {
            JSONObject jsonObject = JSONObject.parseObject(iterator.next().toString());
            if(!jsonObject.getInteger("ChannelId").equals(0)){
                iterator.remove();
            }else {
                jsonObject.put("EndTime", simpleDateFormat.format(jsonObject.getLong("EndTime") - timeDifference));
                jsonObject.put("BeginTime", simpleDateFormat.format(jsonObject.getLong("BeginTime") - timeDifference));
                jsonObjectList.add(jsonObject);
            }
        }
    }
}
